#pragma once

#include "Thread.hpp"
#include "LogMessage.hpp"
#include "lockGuard.hpp"
#include "Task.hpp"

#include <vector>
#include <queue>

const int DEFAULT_SIZE = 5;

template<class T>
class ThreadPool
{
private: // Routine专用接口
    // 获取锁地址
    pthread_mutex_t* _GetMTX()
    {
        return &_mtx;
    }

    // 获取生消信号量地址
    pthread_cond_t* _GetCond()
    {
        return &_cpCond;
    }

    // 判断任务队列中是否为空
    bool _IsEmpty()
    {
        return _taskQueue.empty();
    }

    T _GetTask()
    {
        T task = _taskQueue.front();
        _taskQueue.pop();
        return task;
    }

    // 非static函数会有this指针，这样在创建线程的时候函数指针pfunc会
    // 和非static函数不匹配，报错，所以要改为static
    static void* Routine(void* args)
    {
        // 获取到当前线程池的地址，因为Routine没有this指针，就无法拿到任务
        Thread_name_and_Args* tNA = reinterpret_cast<Thread_name_and_Args*>(args);
        ThreadPool<T>* pt = reinterpret_cast<ThreadPool<T>*>(tNA->_args);
        while(1)
        {
            // 线程执行的任务
            T task;
            {
                // 多个消费者获取任务先上锁
                LockGuard lg(pt->_GetMTX());
                // 上完锁判断是否有任务，没有任务就等
                while(pt->_IsEmpty()) pthread_cond_wait(pt->_GetCond(), pt->_GetMTX());

                // 此处一定可以获取任务
                task = pt->_GetTask();
            }
            // 仿函数执行任务
           task.Run();
        }
    }

private: 
    // 构造私有
    ThreadPool(int size = DEFAULT_SIZE)
        : _size(size)
    {
        // 锁和条件变量初始化
        pthread_cond_init(&_cpCond, nullptr);
        pthread_mutex_init(&_mtx, nullptr);

        // 线程池中创建线程
        for(int i = 0; i < _size; ++i)
        {
            // 线程名字
            std::string name("Thread[");
            name += (std::to_string(i + 1) + ']');

            // 往线程池中加入线程                /*给ThreadData传this指针，不然Routine中线程拿不到任务*/
            _threadPool.push_back(new Thread(name, Routine, this));
        }
    }

    // 删掉拷构和赋构
    ThreadPool(const T& ref) = delete;
    const T& operator=(const T& ref) = delete;

public:
    // 添加任务
    void PushTask(const T& task)
    {
        // 生消互斥，先上锁
        LockGuard lg(&_mtx);
        _taskQueue.push(task);
        // 添加好任务就发送条件信号，让消费者消费
        pthread_cond_signal(&_cpCond);
    }

    // 启动所有线程
    void RunAllThread()
    {
        for(int i = 0; i < _size; ++i)
        {
            _threadPool[i]->CreateThread();
            LogMessage(0, _F, _L, "%s启动成功", _threadPool[i]->getName().c_str());
        }
    }

    // 析构，附加等待线程
    ~ThreadPool()
    {
        for(int i = 0; i < _size; ++i)
        {
            _threadPool[i]->JoinThread();
            delete _threadPool[i];
        }
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cpCond);
    }

    // 懒汉指针接口，必须定义为static的，不然没法创建对象就没法调用
    static ThreadPool<T>* GetThreadPoolPtr(int size = DEFAULT_SIZE)
    {
        if(_threadPoolPtr == nullptr)
        {
            LockGuard LG(&_MTX); // 这个封装在lockGuard.hpp中
            if(_threadPoolPtr == nullptr)
            {
                _threadPoolPtr = new ThreadPool<T>(size);
            }
        }
        
        return _threadPoolPtr;
    }

private:
    // 线程池
    std::vector<Thread*> _threadPool;
    // 线程池大小
    int _size;
    // 任务队列
    std::queue<T> _taskQueue;
    // 消消锁和生消锁
    pthread_mutex_t _mtx;
    // 生消条件变量
    pthread_cond_t _cpCond;

    // 懒汉模式，搞一个指针
    static ThreadPool<T>* _threadPoolPtr;
    // 专门为GetThreadPoolPtr接口提供一个锁
    static pthread_mutex_t _MTX;
};

// 初始情况下设置为nullptr，等用的时候再开空间，此即懒汉
template<class T>
ThreadPool<T>* ThreadPool<T>::_threadPoolPtr = nullptr;

// 全局或静态的锁可以直接用PTHREAD_MUTEX_INITIALIZER进行初始化
template<class T>
pthread_mutex_t ThreadPool<T>::_MTX = PTHREAD_MUTEX_INITIALIZER;

